import { Injectable } from '@nestjs/common';
import { LoggerService } from '@/common/logger/logger.service';
import { UserService } from '../user/user.service';
import { ConfigService } from '@nestjs/config';
import { RedisService } from '@/common/redis/redis.service';
import { LoginAccountDto } from './dto/login-account.dto';
import { LoginEmailDto } from './dto/login-email.dto';
import type { Request } from 'express';
import { getClientIp } from '@/utils/request.info';
import { JwtPayloadInterface } from '@/common/interfaces';
import { plainToClass } from 'class-transformer';
import { ExportLoginDto } from './dto/export-login.dto';
import { JwtService } from '@nestjs/jwt';
import { EnvEnum } from '@/common/enums/env.enum';
import { RedisKeyEnum } from '@/common/enums/redis-key.enum';
import { handleError } from '@/utils/handle.error';
import { TUser } from '@/@types/common';

@Injectable()
export class AuthService {

  constructor(
    private readonly logger: LoggerService,
    private readonly userService: UserService,
    private readonly configService: ConfigService,
    private readonly redisService: RedisService,
    private readonly jwtService: JwtService
  ) {
    this.logger.setContext(AuthService.name);
  }

  /** 登录逻辑 */
  async login(loginAccountDto: LoginAccountDto | LoginEmailDto, req: Request) {
    try {
      this.logger.log(`${this.login.name} was called`);
      // 1. 验证用户信息
      let user;
      if ('name' in loginAccountDto) {
        user = await this.userService.validateUserByPwd(loginAccountDto.name, loginAccountDto.password);
      } else if ('email' in loginAccountDto) {
        user = await this.userService.validateUserByEmail(loginAccountDto.email, loginAccountDto.code);
      }
      const ip = getClientIp(req);
      // 2. 记录用户登录ip
      this.userService.recordUserIp(user.id, ip)

      // 3. 生成token
      const { accessToken, refreshToken } = this.getAccessAndRefreshToken(user.id, user.name, user.email);

      this.redisService.setex(RedisKeyEnum.LoginKey + user.id, 60 * 60, accessToken)

      return {
        ...user,
        accessToken,
        refreshToken
      }
    } catch (error) {
      handleError(this.logger, error, {
        common: '登录失败'
      })
    }
  }

  /** 刷新token */
  async refreshToken(id: number) {
    try {
      this.logger.log(`${this.refreshToken.name} was called`);
      const user = await this.userService.findUserById(id);
      const { accessToken, refreshToken } = this.getAccessAndRefreshToken(user.id, user.name, user.email);
      this.redisService.setex(RedisKeyEnum.LoginKey + user.id, 60 * 60, accessToken)
      return {
        accessToken,
        refreshToken
      }
    } catch (error) {
      handleError(this.logger, error, {
        common: '刷新token失败'
      })
    }
  }

  /** 退出登录 */
  async logout(req: Request) {
    try {
      this.logger.log(`${this.logout.name} was called`);
      const { id, name, email } = req.user as TUser;
      const res = await this.redisService._delKeysContainStr(RedisKeyEnum.LoginKey + id)
      console.log('打印***res', res)
      return res
    } catch (error) {
      handleError(this.logger, error, {
        common: '退出登录失败'
      })
    }

  }

  /** 获取access_token 和refresh_token */
  getAccessAndRefreshToken(id: number, name: string, email: string) {
    this.logger.log(`${this.getAccessAndRefreshToken.name} was called`);
    const payload: JwtPayloadInterface = { id, name, email };
    return plainToClass(ExportLoginDto, {
      accessToken: this.jwtService.sign(payload, {
        expiresIn: this.configService.get(EnvEnum.JWT_ACCESS_TOKEN_EXPIRES_IN)
      }),
      refreshToken: this.jwtService.sign({ id }, {
        expiresIn: this.configService.get(EnvEnum.JWT_REFRESH_TOKEN_EXPIRES_IN)
      })
    })
  }
}
